﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Drawing;

namespace DirectX_CS
{
	public class Simple3dCircle : IManagedVertexBuffer, IManagedMaterial, IManagedModelTransformable, IColorable, IRangeModifiable, IDisposable
	{
		[PGBrowsable]
		public Range3d Range
		{
			get { return new Range3d(XMin, YMin, ZMin, XMax, YMax, ZMax); }
		}
		protected bool mEnableCallRenderPrimitives = true;
		[PGBrowsable]
		public bool EnableCallRenderPrimitives
		{
			get { return mEnableCallRenderPrimitives; }
			set { mEnableCallRenderPrimitives = value; }
		}
		protected bool mEnableCallRenderIndexedPrimitives = true;
		[PGBrowsable]
		public bool EnableCallRenderIndexedPrimitives
		{
			get { return mEnableCallRenderIndexedPrimitives; }
			set { mEnableCallRenderIndexedPrimitives = value; }
		}
		[PGBrowsable]
		public VertexFormats VertexFormat
		{
			get { return SVector.Format; }
		}
		public event VBChangedDelegate VertexBufferChanged;

        private PrimitiveManager renderer = null; //main memory manager..change name.

        public PrimitiveManager Renderer
        {
            get { return renderer; }
            set { renderer = value; }
        }
        private bool mRegistered = false;

        public bool Registered
        {
            get { return mRegistered; }
            set { mRegistered = value; }
        }

        private Device mDevice = null;

        public Device Device
        {
            get { return mDevice; }
            set { mDevice = value; }
        }
		protected SVector[] vertexData = null;

        private Color mColor = Color.DarkCyan;
		[PGBrowsable]
        public Color MainVertexColor
        {
            get { return mColor; }
            set { mColor = value; }
        }
        public const double PI = Math.PI;
		
        protected Material mMaterial;
		[PGBrowsable]
		public Material Material
        {
            get {return mMaterial;}
			set {mMaterial = value; }
        }
        private float mRadius = 5;
        private float mZComponent = 0;
		[PGBrowsable]
        public float Radius
        {
            get { return mRadius; }
            set { mRadius = value; }
        }

        public Simple3dCircle()
        {
            renderer = PrimitiveManager.OnlyManager;
            mDevice = MainProgram.DxDevice; //set before registering for sure...

			InitializeMaterial();

			CreateCircleOverXY(mRadius, mZComponent, mColor); //usually we can register right after making the data, and its a good policy so we dont forget to do set it.
			
			Registered = PrimitiveManager.Register(this);
        }
		/// <summary>
		/// BEGINNING OF IMPLEMENTATION of the interface..
		/// </summary>

		/// <summary>
		/// Careful because registering the primitiveWithVertices generator allocates memory for the primitives it generates, 
		/// and sometimes(depending on the settings) attempts to render the objects in its registered list.
		/// </summary>
		public Simple3dCircle(float radius, float zComponent, Color pixelColor)
		{
            mColor = pixelColor;
			InitializeMaterial();

            mRadius = radius;
            mZComponent = zComponent;
			//if (radius == 0)
			//	throw new Exception("Cartesian3dCircle::Cartesian3dCircle(): radius==0");
			
			renderer = PrimitiveManager.OnlyManager;
			mDevice = MainProgram.DxDevice; //set before registering for sure...

			CreateCircleOverXY(radius, zComponent, mColor); //usually we can register right after making the data, and its a good policy so we dont forget to do set it.
			
			PrimitiveManager.Register(this);
            Registered = true;
		}

		protected void InitializeMaterial()
		{
			Color c = Color.Maroon;
			mMaterial = new Material();
			//mMaterial.Emissive = Color.Firebrick;
			mMaterial.Specular = Color.FromArgb(122, 255, 255, 255);
			mMaterial.Ambient = Color.FromArgb(122, 255, 255, 255);
			mMaterial.Diffuse = Color.FromArgb(122, 255, 255, 255);
		}

		~Simple3dCircle()
		{
			DisposeInstance();
		}

		public void DisposeInstance()
		{
			if (renderer != null)
				renderer.UnregisterPrimitive(this); //cannot hurt to unregister.
		}
        public void UnregisterPrimitive()
        {
            if (renderer != null)
                renderer.UnregisterPrimitive(this); //cannot hurt to unregister.
        }

		public Array GetVertices()
		{
			return (Array)vertexData;
		}
		public int GetVerticesCount()
		{
			return vertexData.Length;
		}

		/// <summary>
		/// Since each primitiveWithVertices should for diversity be in charge of its own representation
		/// in rendering, although the memory will be managed.
		/// </summary>
		public void RenderPrimitives(PrimitiveManager caller, int dataLength)
        {
            mDevice.DrawPrimitives(PrimitiveType.LineStrip, 0, dataLength - 1); //was dataLength-1);
		}

		int angleIterations = 64;
		[PGBrowsable]
		public int AngleIterations
		{
			get { return angleIterations; }
			set { angleIterations = value; }
		}

		long[] GetIndices()
		{
			return null;
		}

		public void ManagerDestroyed(PrimitiveManager caller)
		{
			//signals to registered primitiveWithVertices generator that renderer is or has been destroyed
			//so we should stop sending messages or at least relaying them to the renderer
			renderer = null;
		}
		/// <summary>
		/// End of required implementation;
		/// </summary>
		/// 
		public void Recreate()
		{
			CreateCircleOverXY();
		}

        public void CreateCircleOverXY()
        {
            CreateCircleOverXY(mRadius, mZComponent, mColor);
        }

        public void CreateCircleOverXY(float radius)
        {
            CreateCircleOverXY(radius, mZComponent, mColor);       
        }

		/// <summary>
		/// Create the height data vertex by vertex.
		/// </summary>
		public void CreateCircleOverXY(float radius, float z, Color c)
		{
			//mMaterial = new Material();
			//mMaterial.Specular = c;
			//mMaterial.Diffuse = c;
			//mMaterial.Ambient = c;
			//mMaterial.DiffuseColor = new ColorValue(c.R, c.G, c.B);

			SVector[] dataVerts = new SVector[AngleIterations+1];
			double a = 0;
			double radiansPerIteration = (2f * Math.PI) / AngleIterations;
			for (int dataVertIdx = 0; dataVertIdx <= AngleIterations; dataVertIdx++)
			{
				a = (float)dataVertIdx * radiansPerIteration;
				//float nx,ny,nz;
				//nx = ny = nz = 0;
                int color = c.ToArgb();
                dataVerts[dataVertIdx] = new SVector((radius * cosf(a)), (radius * sinf(a)), z, color);
			}

			///BEGIN: BE SURE TO CALL... WHEN HAVE NEW DATA; OTHERWISE THE DATA WILL ONLY GET QUERIED ONCE.
			vertexData = dataVerts;
			///END: BE SURE TO CALL... AT END OF DATA CREATION. 

            if (!Registered)
            {
                PrimitiveManager.Register(this);
                Registered = true;
            }
            //else
              
            {
                if (VertexBufferChanged != null)
                    VertexBufferChanged(this, vertexData.Length);
            }
		}
		public void UpdateTransform()
		{
			VertexBufferChanged(this, vertexData.Length);
		}
        private static float cosf(double d)
        {
            return (float)Math.Cos(d);
        }
        private static double cos(double d)
        {
            return Math.Cos(d);
        }
        private static double sin(double d)
        {
            return (float)Math.Sin(d);
        }
        private static float sinf(double d)
        { 
            return (float)Math.Sin(d); 
        }



		#region IRangeableOld Members

		public float XMin
		{
			get
			{
				return -abs(Radius);
			}
			set
			{
				return;
			}
		}

		private float abs(float radius)
		{
			return (float)Math.Abs(radius);
		}

		public float XMax
		{
			get
			{
				return abs(Radius);
			}
			set
			{
				return;
			}
		}

		public float YMin
		{
			get
			{
				return -abs(Radius);
			}
			set
			{
				return;
			}
		}

		public float YMax
		{
			get
			{
				return abs(Radius);
			}
			set
			{
				return;
			}
		}

		public float ZMin
		{
			get
			{
				return -abs(Radius);
			}
			set
			{
				return;
			}
		}

		public float ZMax
		{
			get
			{
				return abs(Radius);
			}
			set
			{
				return;
			}
		}

		#endregion

		#region IModelTransformable Members

		TransformMatrix modelView = new TransformMatrix();
		public TransformMatrix ModelView
		{
			get
			{
				return modelView;
			}
			set
			{
				modelView = value;
			}
		}

		#endregion
	}//end of class..
}//end of namespace.
